package session;

import entity.Category;
import entity.Item;
import entity.Utente;
import entity.Wish;
import java.util.Collection;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 * Rappresenta il gestore delle transazioni degli entity Utente.
 *
 * @author Rachid Ouchary, Fabio Strocco, Max Passaghe, Marco Tansini.
 */
@Stateless
public class UtenteFacade implements UtenteFacadeRemote {

    @EJB(name = "categoryFacade")
    private CategoryFacadeRemote categoryFacade;
    @EJB
    private ItemFacadeRemote itemFacade;
    @EJB
    private WishFacadeRemote wishFacade;
    @PersistenceContext(unitName = "WishLister-ejbPU")
    private EntityManager em;

    /**
     * Rende persistente un nuovo Utente.
     *
     * @param utente l'utente da rendere persistente.
     */
    public void create(Utente utente) {
        em.persist(utente);
    }

    /**
     * Aggiorna lo stato dell'utente nel DB allo stato dell'utente passato
     * come parametro.
     *
     * @param utente l'utente da aggiornare.
     */
    public void edit(Utente utente) {
        em.merge(utente);
    }

    /**
     * Rimuove un utente dal DB.
     *
     * @param utente l'utente da rimuovere.
     */
    public void remove(Utente utente) {
        em.remove(em.merge(utente));
    }

    /**
     * Carica l'utente con lo username specificato.
     *
     * @param username lo username dell'utente da caricare.
     * @return l'utente con lo username specificato, <code>null</code> se non esiste nel
     * DB.
     */
    public Utente find(String username) {
        Utente utente = em.find(Utente.class, username);
        Collection<Utente> amici = null;
        if (utente != null) {
            utente.getAmici();
        }
        if (amici != null) {
            amici.size();
        }
        return utente;
    }

    /**
     *
     * @return
     */
    public List<Utente> findAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Utente.class));
        return em.createQuery(cq).getResultList();
    }

    /**
     *
     * @param range
     * @return
     */
    public List<Utente> findRange(int[] range) {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Utente.class));
        Query q = em.createQuery(cq);
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    /**
     *
     * @return
     */
    public int count() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        Root<Utente> rt = cq.from(Utente.class);
        cq.select(em.getCriteriaBuilder().count(rt));
        Query q = em.createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

    /**
     * Verifica se esiste nel DB l'utente specificato.
     *
     * @param utente l'utente di cui verificare l'esistenza nel DB.
     * @return <code>true</code> se l'utente esiste, <code>false</code>
     * altrimenti.
     */
    public boolean exists(Utente utente) {
        return (find(utente.getUsername()) != null);

    }

    /**
     * Carica un utente dal DB solo se esiste un utente nel DB con username
     * e password specificati.
     *
     * @param username lo username dell'utente.
     * @param password la password dell'utente.
     * @return l'utente corrispondente, <code>null</code> se l'utente con
     * username e password specificati non esiste.
     */
    public Utente carica(String username, String password) {
        Utente t = find(username);

        if (t == null) {
            return null;
        }
        if (password.equals(t.getPassword())) {
            return t;
        } else {
            return null;
        }
    }

    /**
     * Aggiunge un amico all'utente specificato.
     *
     * @param utente username dell'utente.
     * @param amico username dell'amico da aggiungere all'utente specificato.
     */
    public void aggiungiAmico(String utente, String amico) {
        if (!utente.equals(amico)) {
            Utente user = find(utente);
            Utente friend = find(amico);
            if (!user.getAmici().contains(friend)) {
                user.aggiungiAmico(friend);
                edit(user);
            }
        }
    }

    /**
     * Cerca tutti i desideri dell'utente specificato ordinati per priorit&agrave.
     *
     * @param username lo username dell'utente.
     * @return la lista dei desideri dell'utente.
     */
    public List<Wish> getOrderedUserWishList(String username) {
        Query q = em.createNamedQuery(Utente.FIND_ORDERED_WISH);
        q.setParameter("username", username);
        return q.getResultList();
    }

    /**
     * Cerca gli amici dell'utente con il nome specificato.
     *
     * @param currentUsername Lo username dell'utente corrente.
     * @param username le iniziali dello username degli amici da cercare.
     * @param nome l'iniziale del nome degli amici da cercare.
     * @param cognome l'iniziale del cognome degli amici da cercare.
     * @param email l'iniziale dell'email degli amici da cercare.
     * @param soloAmici indica se cercare solo tra la lista di amici dell'utente corrente.
     * @return la lista di amici trovati.
     */
    public List<Utente> cercaAmici(String currentUsername, String username, String nome,
            String cognome, String email,
            boolean soloAmici) {
        if (soloAmici) {
            Query q = em.createNamedQuery(Utente.CERCA_AMICI);
            q.setParameter("username", "%" + username.toLowerCase() + "%");
            q.setParameter("nome", "%" + nome.toLowerCase() + "%");
            q.setParameter("cognome", "%" + cognome.toLowerCase() + "%");
            q.setParameter("email", "%" + email.toLowerCase() + "%");
            q.setParameter("current", currentUsername);
            return q.getResultList();
        } else {
            Query q = em.createNamedQuery(Utente.CERCA_PERSONE);
            q.setParameter("username", "%" + username.toLowerCase() + "%");
            q.setParameter("nome", "%" + nome.toLowerCase() + "%");
            q.setParameter("cognome", "%" + cognome.toLowerCase() + "%");
            q.setParameter("email", "%" + email.toLowerCase() + "%");
            q.setParameter("current", (currentUsername == null ? "" : currentUsername));
            q.setParameter("on", currentUsername == null);
            return q.getResultList();
        }
    }

    /**
     * Aggiunge un desiderio all'utente. Si assume che l'oggetto desiderato esista
     * gi&agrave nel DB.
     *
     * @param username lo username dell'utente in cui aggiungere il desiderio.
     * @param item_id l'id dell'oggetto desiderato.
     * @param priority la priorit&agrave del desiderio.
     * @param voto il voto del desiderio.
     */
    public boolean aggiungiDesiderio(String username, Long item_id, Integer priority,
                                  Integer voto) {
        Item item = itemFacade.find(item_id);
        Wish wish = new Wish(priority, voto, item);
        Utente user = find(username);
        
        wish.attachUtente(user);
        if(wishFacade.findWish(wish) != null) {
            return false;
        }
        wishFacade.create(wish);
        wish = wishFacade.findWish(wish);
        user.aggiungiDesiderio(wish);
        wishFacade.edit(wish);
        edit(user);
        return true;
    }

    /**
     * Aggiunge un nuovo desiderio all'utente specificato. L'item desiderato
     * viene creato e memorizzato nel DB se un oggetto equivalente non &egrave
     * presente nel DB.
     *
     * @param username lo username dell'utente in cui aggiungere il desiderio.
     * @param categoria la categoria dell'oggetto desiderato.
     * @param descrizione la descrizione dell'oggetto desiderato.
     * @param proiority la priorit&agrave dell'oggetto desiderato.
     * @param voto il voto dell'oggetto desiderato.
     */
    public boolean creaDesiderio(String username, Long categoryId, String descrizione,
        Integer proiority, Integer voto) {
        Category categoria = categoryFacade.find(categoryId);
        Item item = new Item(categoria, descrizione);
        if (itemFacade.findBy(categoria, descrizione) == null) {
            itemFacade.create(item);
        } else {
            itemFacade.edit(item);
        }
        item = itemFacade.findBy(categoria, descrizione);

        Wish wish = new Wish(proiority, voto, item);

        Utente utente = find(username);
        wish.attachUtente(utente);
        if(wishFacade.findWish(wish) != null) {
            return false;
        }
        wishFacade.create(wish);
        wish = wishFacade.findWish(wish);
        utente.aggiungiDesiderio(wish);
        
        wishFacade.edit(wish);
        edit(utente);
        return true;
    }
}
